﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using Blackbird.Core.Editable.Base;
using Blackbird.Studio.Mvvm;

namespace Blackbird.Studio.ViewModel.EntityViewModels
{
    public abstract class EntityCollectionViewModel<TModel, TChild, TChildViewModel> : EntityViewModel<TModel>, IEntityCollectionViewModel, IList<TChildViewModel>
        where TModel : EditableEntityCollection<TChild>, IEditableEntity
        where TChild : class, IEditableEntity, new()
        where TChildViewModel : EntityViewModel<TChild>
    {
        private bool _isExpanded;
        private readonly ObservableCollection<TChildViewModel> _viewModels;

        public IEnumerable<IEntityViewModel> ViewModels { get { return _viewModels; } } 

        protected EntityCollectionViewModel(TModel model)
            : base(model)
        {
            _viewModels = new ObservableCollection<TChildViewModel>();
            _viewModels.CollectionChanged += ViewModelsCollectionChanged;
        }

        private void ViewModelsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:

                    foreach (TChildViewModel newItem in e.NewItems)
                    {
                        newItem.ParentViewModel = this;
                        newItem.PropertyChanged += ChildPropertyChanged;
                        Model.Add(newItem.Model);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (TChildViewModel removedItem in e.OldItems)
                    {
                        removedItem.ParentViewModel = null;
                        removedItem.PropertyChanged -= ChildPropertyChanged;
                        Model.Remove(removedItem.Model);
                    }
                    break;
            }
        }

        private TChildViewModel _selectedItem;
        public TChildViewModel SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (value != _selectedItem)
                {
                    _selectedItem = value;
                    if (_selectedItem != null)
                    {
                        Model.SelectedItem = SelectedItem.Model;
                        SelectedItem.IsSelected = true;
                    }
                    else
                    {
                        Model.SelectedItem = null;
                    }
                    CreateCommands();
                    InvokePropertyChanged("SelectedItem");
                }
            }
        }
        
        public EntityCommandViewModel AddChildCommand { get; private set; }
        public EntityCommandViewModel RemoveChildCommand { get; private set; }
        public EntityCommandViewModel SelectChildCommand { get; private set; }

        public bool IsExpanded
        {
            get { return _isExpanded; }
            set
            {
                if (_isExpanded != value)
                {
                    _isExpanded = value;
                    InvokePropertyChanged("IsExpanded");
                }
            }
        }

        public override void Initialize() {
            base.Initialize();
            AddChildCommandVisibility = Visibility.Visible;
        }

        public virtual void NewChild(object param)
        {
            var newViewModel = (param as TChildViewModel);
            if (newViewModel == null)
            {
                var newModel = Model.NewChild(null);
                newViewModel = (TChildViewModel)Activator.CreateInstance(typeof(TChildViewModel), newModel);
                newViewModel.Initialize();
            }

            _viewModels.Add(newViewModel);

            IsExpanded = true;
            SelectedItem = newViewModel;

        }

        protected virtual void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsSelected")
            {
                var childViewModel = sender as TChildViewModel;
                if (childViewModel != null && childViewModel.IsSelected)
                    SelectChild(sender);
            }
        }

        private void RemoveChild(object param)
        {
            var viewModel = (param as TChildViewModel);
            if (viewModel != null)
            {
                _viewModels.Remove(viewModel);
                Model.Remove(viewModel.Model);
            }
        }

        private void SelectChild(object param)
        {
            var childViewModel = (TChildViewModel)param;
            SelectedItem = childViewModel;
        }

        internal override void CreateCommands()
        {
            base.CreateCommands();
            var newItem = new TChild();
            var childViewModel =
                (TChildViewModel)Activator.CreateInstance(typeof(TChildViewModel), newItem);
            RegisterCommand(
                AddChildCommand =
                new EntityCommandViewModel("Add " + childViewModel.EntityType,
                                           new RelayCommand(NewChild))
                    {
                        Category = "Add",
                        EntityViewModel = this
                    });

            RemoveChildCommand = new EntityCommandViewModel("Remove " + childViewModel.EntityType,
                                                            new RelayCommand(RemoveChild))
                                     {
                                         Category = "Remove",
                                         EntityViewModel = this
                                     };
            SelectChildCommand = new EntityCommandViewModel("Select " + childViewModel.EntityType,
                                                            new RelayCommand(SelectChild))
                                     {
                                         Category = "Select",
                                         EntityViewModel = this
                                     };
        }

        protected override ObservableCollection<EntityCommandViewModel> GetCommands()
        {
            ObservableCollection<EntityCommandViewModel> allCommands = base.GetCommands();
            if (SelectedItem != null)
                foreach (var command in SelectedItem.RegisteredCommands)
                    allCommands.Add(command);

            return allCommands;
        }

        public override void ReceiveMessage<TSender>(TSender sender, string message)
        {
            base.ReceiveMessage(sender, message);
            var entityViewModel = (sender as TChildViewModel);
            if ((entityViewModel != null) && _viewModels.Contains(entityViewModel) && (message == "IsSelected") && (entityViewModel.IsSelected))
            {
                SelectChild(entityViewModel);
            }
        }

        public virtual IEnumerator<TChildViewModel> GetEnumerator()
        {
            return _viewModels.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public virtual void Add(TChildViewModel item)
        {
            _viewModels.Add(item);
        }

        public virtual void Clear()
        {
            _viewModels.Clear();
        }

        public virtual bool Contains(TChildViewModel item)
        {
            return _viewModels.Contains(item);
        }

        public virtual void CopyTo(TChildViewModel[] array, int arrayIndex)
        {
            _viewModels.CopyTo(array, arrayIndex);
        }

        public virtual bool Remove(TChildViewModel item)
        {
            return _viewModels.Remove(item);
        }

        public virtual int Count
        {
            get { return _viewModels.Count; }
        }

        public virtual bool IsReadOnly
        {
            get { return false; }
        }

        public virtual int IndexOf(TChildViewModel item)
        {
            return _viewModels.IndexOf(item);
        }

        public virtual void Insert(int index, TChildViewModel item)
        {
            _viewModels.Insert(index, item);
        }

        public virtual void RemoveAt(int index)
        {
            _viewModels.RemoveAt(index);
        }

        public virtual TChildViewModel this[int index]
        {
            get { return _viewModels[index]; }
            set { _viewModels[index] = value; }
        }
    }
}